
package com.jf.cloud.multishop.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.jf.cloud.api.leaf.feign.SegmentFeignClient;
import com.jf.cloud.api.multishop.bo.ShopSimpleBO;
import com.jf.cloud.api.multishop.bo.ShopWalletBO;
import com.jf.cloud.common.constant.Constant;
import com.jf.cloud.common.constant.DistributedIdKey;
import com.jf.cloud.common.database.dto.PageDTO;
import com.jf.cloud.common.database.util.PageUtil;
import com.jf.cloud.common.database.vo.PageVO;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.mongodb.util.MongoPageUtil;
import com.jf.cloud.common.response.ResponseEnum;
import com.jf.cloud.common.response.ServerResponseEntity;
import com.jf.cloud.common.util.DateUtils;
import com.jf.cloud.common.util.Json;
import com.jf.cloud.common.util.PriceUtil;
import com.jf.cloud.multishop.bo.mongo.MongoShopWalletLogBO;
import com.jf.cloud.multishop.constant.ShopWalletAmountType;
import com.jf.cloud.multishop.constant.ShopWalletChangeReason;
import com.jf.cloud.multishop.constant.ShopWalletIoType;
import com.jf.cloud.multishop.constant.ShopWalletZoneConstant;
import com.jf.cloud.multishop.dto.ShopWalletLogSearchDTO;
import com.jf.cloud.multishop.mapper.ShopWalletMapper;
import com.jf.cloud.multishop.model.ShopDetail;
import com.jf.cloud.multishop.service.ShopDetailService;
import com.jf.cloud.multishop.service.ShopWalletLogService;
import com.jf.cloud.multishop.vo.ShopWalletLogExcelVO;
import com.jf.cloud.multishop.vo.ShopWalletVO;
import com.jf.cloud.multishop.vo.mongo.MongoShopWalletLogVO;
import com.jf.cloud.multishop.vo.mongo.MongoShopWalletVO;
import com.mongodb.client.result.UpdateResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 商家钱包记录
 *
 * @author zz
 * @date 2021-01-07 16:41:03
 */
@Service
public class ShopWalletLogServiceImpl implements ShopWalletLogService {

    private final Logger logger = LoggerFactory.getLogger(ShopWalletLogServiceImpl.class);

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private SegmentFeignClient segmentFeignClient;
    @Autowired
    private ShopDetailService shopDetailService;
    @Autowired
    private MongoPageUtil mongoPageUtil;
    @Autowired
    private ShopWalletMapper shopWalletMapper;

    @Override
    public PageVO<MongoShopWalletLogBO> pageByParam(PageDTO pageDTO, ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        if (Objects.isNull(shopWalletLogSearchDTO.getReason()) && Objects.nonNull(shopWalletLogSearchDTO.getShopId())) {
            criteria.and("reason").ne(ShopWalletChangeReason.APPLY_RECHARGE.value());
        }
        // 平台搜索
        if (Objects.isNull(shopWalletLogSearchDTO.getShopId())) {
            if (Objects.isNull(shopWalletLogSearchDTO.getAmountType())) {
                // 平台搜索只需要查待结算金额和结算金额的记录
                criteria.and("amountType").in(ShopWalletAmountType.UNSETTLED_AMOUNT.value(), ShopWalletAmountType.SETTLED_AMOUNT.value());
            }
            if (Objects.isNull(shopWalletLogSearchDTO.getReason())) {
                // 平台搜索只需要查订单支付，订单结算，用户退款， 预售失败结算（超时未支付尾款）的记录
                criteria.and("reason")
                        .in(ShopWalletChangeReason.PAY.value(), ShopWalletChangeReason.SETTLED.value(), ShopWalletChangeReason.ORDER_REFUND.value(), ShopWalletChangeReason.SUCCESS_QRCODE_RECHARGE.value(),ShopWalletChangeReason.PRE_SALE_FAIL.value());
            }
        }
        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));
        return mongoPageUtil.doPage(MongoShopWalletLogBO.class, pageDTO, query);
    }

    @Override
    public MongoShopWalletLogVO listActualByHour(Long shopId, Date startTime, Date endTime) {
        GroupOperation groupOperation = Aggregation.group()
                .sum("shopChangeAmount").as("shopLogCount")
                .sum("platformChangeAmount").as("totalAmount");
        // 过滤
        Criteria criteria = Criteria.where("orderId").isNull();
        filterQuery(criteria, shopId, startTime, endTime);

        ProjectionOperation project = Aggregation.project("totalAmount", "shopLogCount");
        // 按顺序组合每一个聚合步骤
        TypedAggregation<MongoShopWalletLogVO> typedAggregation =
                Aggregation.newAggregation(
                        MongoShopWalletLogVO.class,
                        Aggregation.match(criteria),
                        groupOperation,
                        project
                );
        logger.info("进入收款码金额的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoShopWalletLogVO> aggregationResults =
                mongoTemplate.aggregate(typedAggregation, MongoShopWalletLogVO.class);
        // 取出最终结果
        List<MongoShopWalletLogVO> list = aggregationResults.getMappedResults();
        if (!Objects.isNull(list) && list.size()>0) {
            return list.get(0);
        } else {
            return new MongoShopWalletLogVO();
        }

    }

    private void filterQuery(Criteria criteria, Long shopId, Date startTime, Date endTime) {
        if (Objects.nonNull(shopId)) {
            criteria.and("shopId").is(shopId);
        }
        if (Objects.nonNull(startTime) || Objects.nonNull(endTime)) {
            if (Objects.nonNull(startTime) && Objects.nonNull(endTime)) {
                criteria.and("createTime").gte(DateUtils.dateToIsoDate(startTime)).lte(DateUtils.dateToIsoDate(endTime));
            } else if (Objects.nonNull(startTime)) {
                criteria.and("createTime").gte(startTime);
            } else if (Objects.nonNull(endTime)) {
                criteria.and("createTime").lte(endTime);
            }
        }
    }

    @Override
    public MongoShopWalletLogBO getByWalletLogId(Long walletLogId) {
        MongoShopWalletLogBO shopWalletLogDb = mongoTemplate.findById(walletLogId, MongoShopWalletLogBO.class);
        if (Objects.isNull(shopWalletLogDb)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        return shopWalletLogDb;
    }

    @Override
    public List<MongoShopWalletLogBO> listByParam(ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        if (Objects.isNull(shopWalletLogSearchDTO.getShopId()) && StrUtil.isBlank(shopWalletLogSearchDTO.getShopName())) {
            criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);
        }
        if (Objects.isNull(shopWalletLogSearchDTO.getReason())) {
            criteria.and("reason").ne(ShopWalletChangeReason.APPLY_RECHARGE.value());
        }
        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));
        return mongoTemplate.find(query, MongoShopWalletLogBO.class);
    }

    @Override
    public void saveMongo(MongoShopWalletLogBO mongoShopWalletLogBO) {
        ServerResponseEntity<Long> response = segmentFeignClient.getSegmentId(DistributedIdKey.MALL4CLOUD_SHOP_WALLET_LOG);
        if (!response.isSuccess()) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        Long walletLogId = response.getData();
        mongoShopWalletLogBO.setWalletLogId(walletLogId);
        mongoTemplate.insert(mongoShopWalletLogBO);
    }

    @Override
    public void saveMongoBatch(List<MongoShopWalletLogBO> list) {
        if (CollUtil.isEmpty(list)) {
            return;
        }
        // 批量操作
        try {
            mongoTemplate.insert(list, MongoShopWalletLogBO.class);
        } catch (DuplicateKeyException e) {
            logger.info("重复数据插入异常：" + Json.toJsonString(list));
        }
    }

    @Override
    public PageVO<MongoShopWalletLogBO> pageAllShop(PageDTO pageDTO, ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        if (Objects.isNull(shopWalletLogSearchDTO.getShopId()) && StrUtil.isBlank(shopWalletLogSearchDTO.getShopName())) {
            criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);
        }
        if (Objects.isNull(shopWalletLogSearchDTO.getReason())) {
            criteria.and("reason").ne(ShopWalletChangeReason.APPLY_RECHARGE.value());
        }
        Query query = new Query(criteria);
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));

        PageVO<MongoShopWalletLogBO> page = mongoPageUtil.doPage(MongoShopWalletLogBO.class, pageDTO, query);
        // 店铺名称
        List<Long> shopIds = page.getList().stream().map(MongoShopWalletLogBO::getShopId).collect(Collectors.toList());
        List<ShopDetail> shopDetails = shopDetailService.listByShopIds(shopIds);
        Map<Long, String> shopMap = shopDetails.stream().collect(Collectors.toMap(ShopDetail::getShopId, ShopDetail::getShopName));

        for (MongoShopWalletLogBO mongoShopWalletLogVO : page.getList()) {
            if (shopMap.containsKey(mongoShopWalletLogVO.getShopId())) {
                mongoShopWalletLogVO.setShopName(shopMap.get(mongoShopWalletLogVO.getShopId()));
            }
        }
        return page;
    }

    @Override
    public List<ShopWalletLogExcelVO> listShopWalletLogExcel(ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria;
        if (CollUtil.isNotEmpty(shopWalletLogSearchDTO.getShopIds())) {
            criteria = Criteria.where("shopId").in(shopWalletLogSearchDTO.getShopIds());
        } else {
            criteria = getCriteria(shopWalletLogSearchDTO);
        }
        criteria.and("reason").ne(ShopWalletChangeReason.APPLY_RECHARGE.value());
//        if (Objects.isNull(shopWalletLogSearchDTO.getShopId())) {
//            criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);
//        }
        ProjectionOperation project = Aggregation.project("shopId", "orderId", "refundId", "amountType", "shopIoType", "shopChangeAmount", "reason",
                "userAmount", "platformAmount", "distributionAmount", "shopPlatformCommission", "platformCommission", "shopReduceAmount", "createTime");
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.ASC, "shopId").and(Sort.Direction.DESC, "createTime");
        TypedAggregation<MongoShopWalletLogBO> typedAggregation = Aggregation.newAggregation(
                MongoShopWalletLogBO.class,
                Aggregation.match(criteria),
                project,
                sortOperation
        );

        logger.info("listShopWalletLogExcel()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoShopWalletLogBO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoShopWalletLogBO.class);
        // 取出最终结果
        List<MongoShopWalletLogBO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        List<Long> shopIds = list.stream().map(MongoShopWalletLogBO::getShopId).collect(Collectors.toList());
        List<ShopDetail> shopDetails = shopDetailService.listByShopIds(shopIds);
        Map<Long, String> shopMap = shopDetails.stream().collect(Collectors.toMap(ShopDetail::getShopId, ShopDetail::getShopName));
        List<ShopWalletLogExcelVO> result = new ArrayList<>();
        int index = 1;
        for (MongoShopWalletLogBO mongoShopWalletLogBO : list) {
            ShopWalletLogExcelVO shopWalletLogExcel = new ShopWalletLogExcelVO();
            shopWalletLogExcel.setSeq(String.valueOf(index));
            shopWalletLogExcel.setShopName(shopMap.get(mongoShopWalletLogBO.getShopId()));
            shopWalletLogExcel.setAmountType(ShopWalletAmountType.getAmountTypeName(mongoShopWalletLogBO.getAmountType()));
            shopWalletLogExcel.setIoType(ShopWalletIoType.getIoName(mongoShopWalletLogBO.getShopIoType()));
            shopWalletLogExcel.setChangeAmount(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getShopChangeAmount()).doubleValue());
            shopWalletLogExcel.setOrderId(Objects.isNull(mongoShopWalletLogBO.getOrderId()) ? "" : mongoShopWalletLogBO.getOrderId().toString());
            shopWalletLogExcel.setRefundId(Objects.isNull(mongoShopWalletLogBO.getRefundId()) ? "" : mongoShopWalletLogBO.getRefundId().toString());
            shopWalletLogExcel.setUserAmount(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getUserAmount()).doubleValue());
            shopWalletLogExcel.setPlatformAmount(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getPlatformAmount()).doubleValue());
            shopWalletLogExcel.setShopReduceAmount(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getShopReduceAmount()).doubleValue());
            shopWalletLogExcel.setDistributionAmount(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getDistributionAmount()).doubleValue());
            shopWalletLogExcel.setPlatformCommission(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getPlatformCommission()).doubleValue());
            shopWalletLogExcel.setShopPlatformCommission(PriceUtil.toDecimalPrice(mongoShopWalletLogBO.getShopPlatformCommission()).doubleValue());
            shopWalletLogExcel.setReasonStr(ShopWalletChangeReason.getReason(mongoShopWalletLogBO.getReason()));
            shopWalletLogExcel.setCreateTime(mongoShopWalletLogBO.getCreateTime());
            result.add(shopWalletLogExcel);
            index++;
        }
        return result;
    }

    @Override
    public PageVO<MongoShopWalletLogBO> getRechargePageByShopId(PageDTO pageDTO, Long shopId) {
        Query query = new Query();
        query.addCriteria(Criteria.where("shopId").is(shopId)
                .and("reason").is(ShopWalletChangeReason.SUCCESS_RECHARGE.value()));
        query.with(Sort.by(Sort.Direction.DESC, "createTime"));
        return mongoPageUtil.doPage(MongoShopWalletLogBO.class, pageDTO, query);
    }

    @Override
    public long countInAmountByOrderId(Long orderId, Integer shopWalletChangeReason) {
        return countInAmountByOrderIds(Collections.singletonList(orderId), shopWalletChangeReason);
    }

    @Override
    public Long countInAmountByOrderIds(List<Long> orderIds, Integer shopWalletChangeReason) {
        Criteria criteria = Criteria.where("orderId").in(orderIds).and("reason").is(shopWalletChangeReason);
        return countByMongoDb(criteria);
    }

    @Override
    public long countOutAmountByRefundId(Long refundId, Integer shopWalletChangeReason) {
        Criteria criteria = Criteria.where("refundId").is(refundId).and("reason").is(shopWalletChangeReason);
        return countByMongoDb(criteria);
    }

    @Override
    public void updateRechargeSuccess(Long shopWalletLogId, Long orderId) {
        MongoShopWalletLogBO shopWalletLogDb = mongoTemplate.findById(shopWalletLogId, MongoShopWalletLogBO.class);
        if (Objects.isNull(shopWalletLogDb)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        // 幂等
        if (Objects.equals(shopWalletLogDb.getReason(), ShopWalletChangeReason.SUCCESS_RECHARGE.value())) {
            return;
        }

        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(shopWalletLogId).and("reason").is(ShopWalletChangeReason.APPLY_RECHARGE.value()));
        Update update = new Update();
        update.set("orderId", orderId);
        update.set("reason", ShopWalletChangeReason.SUCCESS_RECHARGE.value());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, MongoShopWalletLogBO.class);
        if (updateResult.getModifiedCount() < 1) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public List<Long> listNeedZoneShopIds() {
        Date date = new Date();
        // 分片数量0->10
        // 获取还没分片的店铺id
        List<Long> shopIds = shopWalletMapper.listShopIdsByZoneNum(ShopWalletZoneConstant.ZERO);
        if (CollUtil.isEmpty(shopIds)) {
            return new ArrayList<>(0);
        }
        // 获取五秒前至今创建的店铺日志数量若大于30的店铺id
        Criteria criteria = Criteria.where("shopId").in(shopIds)
                .and("createTime").gte(DateUtil.offsetSecond(date, -5)).lte(date);
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("shopId").count().as("shopLogCount")
                        .last("shopId").as("shopId"));
        List<MongoShopWalletLogVO> mappedResults = mongoTemplate.aggregate(aggregation, MongoShopWalletLogBO.class, MongoShopWalletLogVO.class).getMappedResults();
        // 并发数5秒30
        return mappedResults.stream().filter(wallet -> wallet.getShopLogCount() >= ShopWalletZoneConstant.LOG_NUM)
                .map(MongoShopWalletLogVO::getShopId).collect(Collectors.toList());
    }

    @Override
    public ShopWalletVO getShopWalletByTime(ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);
        ProjectionOperation projectionOperation = Aggregation.project()
                // 待结算金额收入： 用户支付 拒绝用户退款
                .and(ConditionalOperators.when(Criteria.where("reason").in(0, 3)).then(ConditionalOperators.when(Criteria.where("amountType").is(0)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("inUnsettleAmount")
                // 待结算金额支出： 用户退款成功 系统扣除订单中需要颁发给用户的分销金额
                .and(ConditionalOperators.when(Criteria.where("reason").in(2, 9)).then(ConditionalOperators.when(Criteria.where("amountType").is(0)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("outUnsettleAmount")
                // 总结算金额：订单结算 余额充值成功 预售失败结算（超时未支付尾款）
                .and(ConditionalOperators.when(Criteria.where("reason").in(1, 11, 14,15)).then(ConditionalOperators.when(Criteria.where("amountType").is(1)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("totalSettleAmount")
                // 冻结金额收入：提现申请
                .and(ConditionalOperators.when(Criteria.where("reason").in(4)).then(ConditionalOperators.when(Criteria.where("amountType").is(2)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("inFreezeAmount")
                // 冻结金额支出：提现申请被拒绝 提现申请通过
                .and(ConditionalOperators.when(Criteria.where("reason").in(5, 6)).then(ConditionalOperators.when(Criteria.where("amountType").is(2)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("outFreezeAmount");
        // 分组
        GroupOperation groupOperation = Aggregation.group()
                .sum("inUnsettleAmount").as("inUnsettleAmount")
                .sum("outUnsettleAmount").as("outUnsettleAmount")
                .sum("totalSettleAmount").as("totalSettleAmount")
                .sum("inFreezeAmount").as("inFreezeAmount")
                .sum("outFreezeAmount").as("outFreezeAmount");

        TypedAggregation<MongoShopWalletVO> typedAggregation = Aggregation.newAggregation(
                MongoShopWalletVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                groupOperation
        );

        logger.info("getShopWalletByTime()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoShopWalletVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoShopWalletVO.class);
        // 取出最终结果
        List<MongoShopWalletVO> list = aggregationResults.getMappedResults();
        ShopWalletVO shopWalletVO = new ShopWalletVO();
        if (CollUtil.isEmpty(list)) {
            return shopWalletVO;
        }
        MongoShopWalletVO mongoShopWalletVO = list.get(0);
        shopWalletVO.setUnsettledAmount(mongoShopWalletVO.getInUnsettleAmount() - mongoShopWalletVO.getOutUnsettleAmount());
        shopWalletVO.setTotalSettledAmount(mongoShopWalletVO.getTotalSettleAmount());
        shopWalletVO.setFreezeAmount(mongoShopWalletVO.getInFreezeAmount() - mongoShopWalletVO.getOutFreezeAmount());
        return shopWalletVO;
    }

    @Override
    public ShopWalletBO getQrcodeShopWalletByTime(ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
//        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        //收款码
        Aggregation qrCodeSettledAggregation = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("reason").in(ShopWalletChangeReason.SUCCESS_QRCODE_RECHARGE.value())
                        .and("amountType").is(ShopWalletAmountType.SETTLED_AMOUNT.value())
                        .and("createTime").gte(DateUtils.dateToIsoDate(shopWalletLogSearchDTO.getStartTime()))
                        .lte(DateUtils.dateToIsoDate(shopWalletLogSearchDTO.getEndTime()))),

                Aggregation.group("platformIoType").sum("userAmount").as("totalAmount")
                        .last("platformIoType").as("platformIoType")
        );
        AggregationResults<MongoShopWalletLogVO> qrCodeSettledResults = mongoTemplate.aggregate(qrCodeSettledAggregation, MongoShopWalletLogBO.class, MongoShopWalletLogVO.class);
        logger.info("getQrcodeShopWalletByTime()的mongodb查询语句： {}", qrCodeSettledAggregation);
        List<MongoShopWalletLogVO> qrCodeSettleList = qrCodeSettledResults.getMappedResults();
        long qrCodeAmount=0;
        if (!CollUtil.isEmpty(qrCodeSettleList)) {
            for (MongoShopWalletLogVO mongoShopWalletLogVO : qrCodeSettleList) {
                // 收款码金额
                qrCodeAmount = mongoShopWalletLogVO.getTotalAmount();
            }
        }
        ShopWalletBO shopWalletBO=new ShopWalletBO();
        shopWalletBO.setTotalSettledAmount(qrCodeAmount);
        return shopWalletBO;
    }

    @Override
    public PageVO<ShopWalletVO> pageShopWalletByTime(PageDTO pageDTO, ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = getCriteria(shopWalletLogSearchDTO);
        criteria.and("shopId").ne(Constant.PLATFORM_SHOP_ID);
        ProjectionOperation projectionOperation = Aggregation.project("shopId")
                // 待结算金额收入： 用户支付 拒绝用户退款
                .and(ConditionalOperators.when(Criteria.where("reason").in(0, 3)).then(ConditionalOperators.when(Criteria.where("amountType").is(0)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("inUnsettleAmount")
                // 待结算金额支出： 用户退款成功 系统扣除订单中需要颁发给用户的分销金额
                .and(ConditionalOperators.when(Criteria.where("reason").in(2, 9)).then(ConditionalOperators.when(Criteria.where("amountType").is(0)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("outUnsettleAmount")
                // 总结算金额：订单结算 余额充值成功 收款码 预售失败结算（超时未支付尾款）
                .and(ConditionalOperators.when(Criteria.where("reason").in(1, 11, 14,15)).then(ConditionalOperators.when(Criteria.where("amountType").is(1)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("totalSettleAmount")
                // 冻结金额收入：提现申请
                .and(ConditionalOperators.when(Criteria.where("reason").in(4)).then(ConditionalOperators.when(Criteria.where("amountType").is(2)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("inFreezeAmount")
                // 冻结金额支出：提现申请被拒绝 提现申请通过
                .and(ConditionalOperators.when(Criteria.where("reason").in(5, 6)).then(ConditionalOperators.when(Criteria.where("amountType").is(2)).then("$shopChangeAmount").otherwise(0)).otherwise(0)).as("outFreezeAmount");
        // 分组
        GroupOperation groupOperation = Aggregation.group("shopId")
                .first("shopId").as("shopId")
                .sum("inUnsettleAmount").as("inUnsettleAmount")
                .sum("outUnsettleAmount").as("outUnsettleAmount")
                .sum("totalSettleAmount").as("totalSettleAmount")
                .sum("inFreezeAmount").as("inFreezeAmount")
                .sum("outFreezeAmount").as("outFreezeAmount");

        SortOperation sortOperation = Aggregation.sort(Sort.Direction.ASC, "shopId");
        // 分页数据
        SkipOperation skipOperation = Aggregation.skip((long) pageDTO.getPageSize() * (pageDTO.getPageNum() - 1));
        LimitOperation limitOperation = Aggregation.limit(pageDTO.getPageSize());
        TypedAggregation<MongoShopWalletVO> typedAggregation = Aggregation.newAggregation(
                MongoShopWalletVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                groupOperation,
                sortOperation,
                skipOperation,
                limitOperation
        );

        logger.info("pageShopWalletByTime()的mongodb查询语句： {}", typedAggregation);

        // 执行聚合操作
        AggregationResults<MongoShopWalletVO> aggregationResults = mongoTemplate.aggregate(typedAggregation, MongoShopWalletVO.class);
        // 取出最终结果
        List<MongoShopWalletVO> list = aggregationResults.getMappedResults();
        if (CollUtil.isEmpty(list)) {
            return new PageVO<>();
        }
        // 总条数
        TypedAggregation<MongoShopWalletVO> aggregation = Aggregation.newAggregation(
                MongoShopWalletVO.class,
                Aggregation.match(criteria),
                projectionOperation,
                groupOperation
        );

        // 执行聚合操作
        AggregationResults<MongoShopWalletVO> aggregate = mongoTemplate.aggregate(aggregation, MongoShopWalletVO.class);
        // 取出最终结果
        List<MongoShopWalletVO> mappedResults = aggregate.getMappedResults();
        // 组装数据
        List<ShopWalletVO> result = new ArrayList<>();
        List<Long> shopIds = list.stream().map(MongoShopWalletVO::getShopId).collect(Collectors.toList());
        List<ShopDetail> shopDetails = shopDetailService.listByShopIds(shopIds);
        Map<Long, String> shopMap = shopDetails.stream().collect(Collectors.toMap(ShopDetail::getShopId, ShopDetail::getShopName));
        for (MongoShopWalletVO mongoShopWalletVO : list) {
            ShopWalletVO shopWalletVO = new ShopWalletVO();
            String shopName = shopMap.get(mongoShopWalletVO.getShopId());
            shopWalletVO.setShopId(mongoShopWalletVO.getShopId());
            shopWalletVO.setShopName(shopName);
            shopWalletVO.setUnsettledAmount(mongoShopWalletVO.getInUnsettleAmount() - mongoShopWalletVO.getOutUnsettleAmount());
            shopWalletVO.setTotalSettledAmount(mongoShopWalletVO.getTotalSettleAmount());
            shopWalletVO.setFreezeAmount(mongoShopWalletVO.getInFreezeAmount() - mongoShopWalletVO.getOutFreezeAmount());
            result.add(shopWalletVO);
        }
        PageVO<ShopWalletVO> page = new PageVO<>();
        page.setTotal((long) mappedResults.size());
        page.setList(result);
        page.setPages(PageUtil.getPages(mappedResults.size(), pageDTO.getPageSize()));
        return page;
    }

    @Override
    public ShopWalletVO getPlatformWallet() {
        // 统计reason在0,1,2的所有平台待结算金额,再统计结算金额，减掉就是真正的待结算金额
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("reason")
                        .in(ShopWalletChangeReason.PAY.value(), ShopWalletChangeReason.ORDER_REFUND.value())
                        .and("amountType").is(ShopWalletAmountType.UNSETTLED_AMOUNT.value())),
                Aggregation.group("platformIoType").sum("platformChangeAmount").as("totalAmount")
                        .last("platformIoType").as("platformIoType")
        );
        AggregationResults<MongoShopWalletLogVO> results = mongoTemplate.aggregate(aggregation, MongoShopWalletLogBO.class, MongoShopWalletLogVO.class);
        List<MongoShopWalletLogVO> unSettleList = results.getMappedResults();
        if (CollUtil.isEmpty(unSettleList)) {
            return new ShopWalletVO();
        }

        Aggregation settledAggregation = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("reason").in(ShopWalletChangeReason.SETTLED.value())
                        .and("amountType").is(ShopWalletAmountType.SETTLED_AMOUNT.value())),
                Aggregation.group("platformIoType").sum("platformChangeAmount").as("totalAmount")
                        .last("platformIoType").as("platformIoType")
        );

        AggregationResults<MongoShopWalletLogVO> settledResults = mongoTemplate.aggregate(settledAggregation, MongoShopWalletLogBO.class, MongoShopWalletLogVO.class);
        List<MongoShopWalletLogVO> settleList = settledResults.getMappedResults();
        Long outUnAmount = 0L;
        Long inUnAmount = 0L;
        for (MongoShopWalletLogVO mongoShopWalletLogVO : unSettleList) {
            if (Objects.equals(mongoShopWalletLogVO.getPlatformIoType(), ShopWalletIoType.EXPENDITURE.value())) {
                // 待结算支出金额
                outUnAmount = mongoShopWalletLogVO.getTotalAmount();
            }
            if (Objects.equals(mongoShopWalletLogVO.getPlatformIoType(), ShopWalletIoType.INCOME.value())) {
                // 待结算收入金额
                inUnAmount = mongoShopWalletLogVO.getTotalAmount();
            }
        }
        Long outAmount = 0L;
        Long inAmount = 0L;
        for (MongoShopWalletLogVO mongoShopWalletLogVO : settleList) {
            if (Objects.equals(mongoShopWalletLogVO.getPlatformIoType(), ShopWalletIoType.EXPENDITURE.value())) {
                // 结算支出金额
                outAmount = mongoShopWalletLogVO.getTotalAmount();
            }
            if (Objects.equals(mongoShopWalletLogVO.getPlatformIoType(), ShopWalletIoType.INCOME.value())) {
                // 结算收入金额
                inAmount = mongoShopWalletLogVO.getTotalAmount();
            }
        }

        //收款码
        Aggregation qrCodeSettledAggregation = Aggregation.newAggregation(
                Aggregation.match(Criteria.where("reason").in(ShopWalletChangeReason.SUCCESS_QRCODE_RECHARGE.value())
                        .and("amountType").is(ShopWalletAmountType.SETTLED_AMOUNT.value())),
                Aggregation.group("platformIoType").sum("platformChangeAmount").as("totalAmount")
                        .last("platformIoType").as("platformIoType")
        );
        AggregationResults<MongoShopWalletLogVO> qrCodeSettledResults = mongoTemplate.aggregate(qrCodeSettledAggregation, MongoShopWalletLogBO.class, MongoShopWalletLogVO.class);
        List<MongoShopWalletLogVO> qrCodeSettleList = qrCodeSettledResults.getMappedResults();
        long qrCodeAmount=0;
        if (!CollUtil.isEmpty(qrCodeSettleList)) {
            for (MongoShopWalletLogVO mongoShopWalletLogVO : qrCodeSettleList) {
                    // 收款码金额
                    qrCodeAmount = mongoShopWalletLogVO.getTotalAmount();
            }
        }
        long settledAmount = inAmount - outAmount;
        long unsettleAmount = inUnAmount - outUnAmount - settledAmount;
        ShopWalletVO shopWalletVO = new ShopWalletVO();
        shopWalletVO.setUnsettledAmount(unsettleAmount);
        shopWalletVO.setSettledAmount(settledAmount+qrCodeAmount);
        return shopWalletVO;
    }

    @Override
    public List<MongoShopWalletLogBO> listByOrderIdsAndChangeReason(List<Long> orderIds, Integer shopWalletChangeReason) {
        Criteria criteria = Criteria.where("orderId").in(orderIds).and("reason").is(shopWalletChangeReason);
        Query query = new Query(criteria);
        List<MongoShopWalletLogBO> mongoShopWalletLogList = mongoTemplate.find(query, MongoShopWalletLogBO.class);
        return mongoShopWalletLogList;
    }

    /**
     * 获取MongoDB的搜索条件
     */
    private Criteria getCriteria(ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        Criteria criteria = new Criteria();
        if (Objects.nonNull(shopWalletLogSearchDTO.getOrderId())) {
            criteria.and("orderId").is(shopWalletLogSearchDTO.getOrderId());
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getRefundId())) {
            criteria.and("refundId").is(shopWalletLogSearchDTO.getRefundId());
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getAmountType())) {
            criteria.and("amountType").is(shopWalletLogSearchDTO.getAmountType());
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getReason())) {
            criteria.and("reason").is(shopWalletLogSearchDTO.getReason());
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getShopId())) {
//            criteria.and("shopId").is(shopWalletLogSearchDTO.getShopId());
            List<Long> shopIds = new ArrayList<>();
            shopIds.add(shopWalletLogSearchDTO.getShopId());
            criteria.and("shopId").in(shopIds);
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getShopIoType())) {
            criteria.and("shopIoType").is(shopWalletLogSearchDTO.getShopIoType());
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getPlatformIoType())) {
            criteria.and("platformIoType").is(shopWalletLogSearchDTO.getPlatformIoType());
        }
        if (StrUtil.isNotBlank(shopWalletLogSearchDTO.getShopName())) {
            ShopSimpleBO shopSimpleBO = new ShopSimpleBO();
            shopSimpleBO.setShopName(shopWalletLogSearchDTO.getShopName());
            List<ShopSimpleBO> shopSimpleList = shopDetailService.listSimple(shopSimpleBO);
            if (CollUtil.isNotEmpty(shopSimpleList)) {
                List<Long> shopIds = shopSimpleList.stream().map(ShopSimpleBO::getShopId).collect(Collectors.toList());
                criteria.and("shopId").in(shopIds);
            }
        }
        if (Objects.nonNull(shopWalletLogSearchDTO.getStartTime())) {
            criteria.and("createTime").gte(DateUtils.dateToIsoDate(shopWalletLogSearchDTO.getStartTime())).lte(DateUtils.dateToIsoDate(shopWalletLogSearchDTO.getEndTime()));
        }
        return criteria;
    }

    private long countByMongoDb(Criteria criteria) {
        return mongoTemplate.count(new Query(criteria), MongoShopWalletLogBO.class);
    }
}

